<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>Clipping and masking</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="PyCairo, graphics, Python, 2D vector library, clipping, masking">
<meta name="description" content="In this part of the PyCairo tutorial, we introduce clipping
and masking operations.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container2">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content2">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Clipping &amp; masking</h1>


<p>
In this part of the PyCairo tutorial, we will talk about clipping and 
masking operations. 
</p>

<div class="big_hor">
<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* big_horizontal */
google_ad_slot = "2904953388";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>


<h2>Clipping</h2>

<p>
<b>Clipping</b> is restricting of drawing to a certain area. 
This is done for effeciency reasons and to create interesting effects.
PyCairo has a <code>clip()</code> method to set the clipping. 
</p>


<pre class="code">
#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program shows how to perform
clipping in PyCairo.

author: Jan Bodnar
website: zetcode.com 
last edited: August 2012
'''


from gi.repository import Gtk, GLib
import cairo
import math
import random


class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()
        
        self.init_ui()
        self.load_image()
        self.init_vars()
        
        
    def init_ui(self):    

        self.darea = Gtk.DrawingArea()
        self.darea.connect("draw", self.on_draw)
        self.add(self.darea)
        
        GLib.timeout_add(100, self.on_timer)

        self.set_title("Clipping")
        self.resize(300, 200)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
        
        
    def load_image(self):
        
        self.image = cairo.ImageSurface.create_from_png("beckov.png")
 
        
    def init_vars(self):    
    
        self.pos_x = 128
        self.pos_y = 128
        self.radius = 40

        self.delta = [3, 3]        
        
        
    def on_timer(self):
        
        self.pos_x += self.delta[0]
        self.pos_y += self.delta[1]        
           
        self.darea.queue_draw()
        return True           
        
    
    def on_draw(self, wid, cr):

        w, h = self.get_size()

        if (self.pos_x &lt; 0 + self.radius):
            self.delta[0] = random.randint(5, 9)
        elif (self.pos_x > w - self.radius):
            self.delta[0] = -random.randint(5, 9)
        
        if (self.pos_y &lt; 0 + self.radius): 
            self.delta[1] = random.randint(5, 9)
        elif (self.pos_y > h - self.radius):
            self.delta[1] = -random.randint(5, 9)        

        cr.set_source_surface(self.image, 1, 1)
        cr.arc(self.pos_x, self.pos_y, self.radius, 0, 2*math.pi)
        cr.clip()
        cr.paint()        
        
    
def main():
    
    app = Example()
    Gtk.main()
        
        
if __name__ == "__main__":    
    main()
</pre>

<p>
In this example, we will clip an image. A circle is moving on
the window area and showing a part of the underlying image. 
This is as if we looked through a hole.
</p>

<pre class="explanation">
def load_image(self):
        
    self.image = cairo.ImageSurface.create_from_png("beckov.png")
</pre>

<p>
This is the underlying image. Each timer cycle, we will see a portion
of this image.
</p>

<pre class="explanation">
if (self.pos_x &lt; 0 + self.radius):
    self.delta[0] = random.randint(5, 9)
elif (self.pos_x > w - self.radius):
    self.delta[0]= -random.randint(5, 9) 
</pre>

<p>
If the circle hits the left or the right side of the window, 
the direction of the circle movement changes randomly. Same applies for 
the top and bottom sides.
</p>

<pre class="explanation">
cr.arc(self.pos_x, self.pos_y, self.radius, 0, 2*math.pi)
</pre>

<p>
This line adds a circular path to the Cairo context.
</p>

<pre class="explanation">
cr.clip()
</pre>

<p>
The <code>clip()</code> sets a clipping region. The clipping region is the 
current path used. The current path was created by the <code>arc()</code> 
method call.
</p>

<pre class="explanation">
cr.paint()
</pre>

<p>
The <code>paint()</code> paints the current source everywhere within the 
current clip region.
</p>

<img src="/img/gfx/pycairo/clipping.png" alt="Clipping">
<div class="figure">Figure: Clipping</div>


<h2>Masking</h2>

<p>
Before the source is applied to the surface, it is filtered first. 
The mask is used as a filter. The mask determines where the sourse 
is applied and where not. Opaque parts of the mask allow to copy 
the source. Transparent parts do not let to copy the source 
to the surface.
</p>

<pre class="code">
#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program demonstrates masking.

author: Jan Bodnar
website: zetcode.com 
last edited: August 2012
'''


from gi.repository import Gtk
import cairo


class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()
        
        self.init_ui()
        self.load_image()
        
        
    def init_ui(self):    

        darea = Gtk.DrawingArea()
        darea.connect("draw", self.on_draw)
        self.add(darea)

        self.set_title("Masking")
        self.resize(310, 100)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
        
        
    def load_image(self):    
        
        self.ims = cairo.ImageSurface.create_from_png("omen.png")
        
    
    def on_draw(self, wid, cr):

        cr.mask_surface(self.ims, 0, 0);
        cr.fill()

        
def main():
    
    app = Example()
    Gtk.main()
        
        
if __name__ == "__main__":    
    main()
</pre>

<p>
In the example, the mask determines where to paint and 
where not to paint.
</p>

<pre class="explanation">
cr.mask_surface(self.ims, 0, 0);
cr.fill()
</pre>

<p>
We use an image as a mask, thus displaying it on the window.
</p>

<img src="/img/gfx/pycairo/omen.png" alt="Masking">
<div class="figure">Figure: Masking</div>


<h2>Blind down effect</h2>

<p>
In this code example, we will blind down our image. This is similar to 
what we do with a roller-blind. 
</p>

<pre class="code">
#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program creates a blind down
effect using masking operation.

author: Jan Bodnar
website: zetcode.com 
last edited: August 2012
'''


from gi.repository import Gtk, GLib
import cairo
import math


class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()
        
        self.init_ui()
        self.load_image()
        self.init_vars()
        
        
    def init_ui(self):    

        self.darea = Gtk.DrawingArea()
        self.darea.connect("draw", self.on_draw)
        self.add(self.darea)
               
        GLib.timeout_add(35, self.on_timer)

        self.set_title("Blind down")
        self.resize(325, 250)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
        
        
        
    def load_image(self):
        
        self.image = cairo.ImageSurface.create_from_png("beckov.png")        
               
            
    def init_vars(self):        
        
        self.timer = True
        self.h = 0
        self.iw = self.image.get_width()
        self.ih = self.image.get_height()   
        
        self.ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, 
            self.iw, self.ih)           
        
        
    def on_timer(self):
               
        if (not self.timer):
            return False
                   
        self.darea.queue_draw()
        return True           
        
    
    def on_draw(self, wid, cr):
        
        ic = cairo.Context(self.ims)

        ic.rectangle(0, 0, self.iw, self.h)
        ic.fill()

        self.h += 1
        
        if (self.h == self.ih): 
            self.timer = False

        cr.set_source_surface(self.image, 10, 10)
        cr.mask_surface(self.ims, 10, 10)        
        
    
def main():
    
    app = Example()
    Gtk.main()
        
        
if __name__ == "__main__":    
    main()
</pre>

<p>
The idea behind the blind down effect is quite simple. The image is 
h pixels high. We draw 0, 1, 2 ... lines of 1px height. Each cycle the 
portion of the image is 1px higher, until the whole image is visible.
</p>

<pre class="explanation">
def load_image(self):
    
    self.image = cairo.ImageSurface.create_from_png("beckov.png")            
</pre>

<p>
In the <code>load_image()</code> method, we create an image surface
from a PNG image.
</p>


<pre class="explanation">
def init_vars(self):        
    
    self.timer = True
    self.h = 0
    self.iw = self.image.get_width()
    self.ih = self.image.get_height()   
    
    self.ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, 
        self.iw, self.ih)               
</pre>

<p>
In the init_vars() method, we initiate some variables. We initiate the
self.timer and the self.h variables. We get the width and height of the
loaded image. And we create an empty image surface. It is going to be filled
with lines of pixels from the image surface, that we have created earlier.
</p>

<pre class="explanation">
ic = cairo.Context(self.ims)
</pre>

<p>
We create a cairo context from the empty image source.
</p>

<pre class="explanation">
ic.rectangle(0, 0, self.iw, self.h)
ic.fill()
</pre>

<p>
We draw a rectangle into the initially empty image. The rectangle 
will be 1px higher each cycle. The image created this way will serve as 
a mask later.
</p>

<pre class="explanation">
self.h += 1
</pre>

<p>
The height of the image to show is increased by one unit.
</p>

<pre class="explanation">
if (self.h == self.ih): 
    self.timer = False
</pre>

<p>
We stop the timer method when we draw the whole image on the GTK window.
</p>

<pre class="explanation">
cr.set_source_surface(self.image, 10, 10)
cr.mask_surface(self.ims, 10, 10)
</pre>

<p>
The image of a castle is set as a source for painting. The 
<code>mask_surface()</code> paints the current 
source using the alpha channel of surface as a mask.
</p>


<p>
This chapter covered clipping and masking in PyCairo. 
</p>


<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>
<br>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified August 24, 2012  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>


